package myJs.detailInfo

import cats.effect.IO
import japgolly.scalajs.react.ScalaFnComponent
import japgolly.scalajs.react.util.EffectCatsEffect._
import japgolly.scalajs.react.vdom.all._
import myJs.Implicits._
import myJs.api.Api
import myJs.cps.BootstrapTableCp
import myJs.cps.BootstrapTableCp.Column
import myJs.myPkg.Hooks._
import myJs.myPkg.reactBootstrap._
import myJs.tool.Pojo._
import myJs.tool.Tool
import shared.pojo.Browse._
import upickle.default._

import scala.collection.immutable.SeqMap
import scala.scalajs.js

/** Created by yz on 28/11/2023
  */
object SvDataCp {

  case class Props(id: String, hasChange: Boolean => IO[Unit])

  val Component = ScalaFnComponent[Props] { props =>
    val fileNamePrefix = s"sv.anno"

    val (datas, setDatas) = useState(List.empty[ujson.Obj])
    val (columns, setColumns) = useState(List.empty[Column])

    object FTool {

      def tbFmt(field: String) = (v: ujson.Value, row: ujson.Obj) => {
        field match {
          case "ref_id" =>
            a(href := s"https://www.ncbi.nlm.nih.gov/protein/${v.str}", cursor.pointer, target := "_blank", v.str)
          case "query_ID" =>
            a(href := s"https://www.ncbi.nlm.nih.gov/nuccore/${v.str}", cursor.pointer, target := "_blank", v.str)
          case "gene_ids" =>
            if (v.str.equalsIgnoreCase("NA")) {
              span(v.str)
            } else {
              v.str
                .split(",")
                .map { x =>
                  a(href := s"https://www.ncbi.nlm.nih.gov/gene/?term=${x}", cursor.pointer, target := "_blank", x)
                }
                .mkReactFragment(",")
            }
          case _ => span(v.myStr)()
        }
      }

      def comVFmt(fileTitle: String) = (v: ujson.Value, row: ujson.Obj) => {
        fileTitle match {
          case _ => v.myStr
        }
      }

      def refreshBasicData = {
        val request = GetFileData.Request(id = props.id, fileName = s"${fileNamePrefix}.txt")
        Api.browse.getMummerFileData(request).flatMap { response =>
          val curDatas = response.array
          val map = response.map
          val basicColumns = map.map { case (k, v) =>
            val searchKind = v match {
              case _ => TextSearch
            }
            val title = v match {
              case _ => v
            }
            val fmt = FTool.tbFmt(v)
            val comVF = FTool.comVFmt(v)
            val optionMap = searchKind match {
              case _ => SeqMap[String, String]()
            }
            val operable = k match {
              case _ => true
            }
            val questionMap = SeqMap(
              "ref_id" -> span("Target序列名称"),
              "ref_start" -> span("SV在Target序列上起点"),
              "ref_end" -> span("SV在Target序列上的终点"),
              "query_ID" -> span("Query序列名称"),
              "sv_start" -> span("SV在Query序列上起点"),
              "sv_end" -> span("SV在Query序列上的终点"),
              "strand" -> span("比对方向"),
              "sv_type" -> div(
                "SV类型：Translocation（易位）、Inversion（倒置）、Trans+Inver(易位兼倒置) 、Deletion（缺失）、Insertion（插入）、ComplexInDel（Target与Query基因组位置上相对应的序列比对不上，其形成原因可能是target和queryt都在指定的区域发生了插入，或者其中之一发生了插入和缺少，而且发生的区域有所重叠，也有可能是该区域的变异太大了导致比对不上）、DupInser(串联重复序列插入)、DupDele(串联重复序列缺失)",
                br(),img(src:=s"${Api.images}/sv_1.png",width:="100%"),
                br(),"Insertion：插入，Deletion：缺失 Insertion、Deletion后面加数字时表示，该InDel位于重复序列区，数字的大小表示InDel可以向后延伸的距离；DupInsert、DupDele后面的数字表示重复序列在缺失的基因组上的长度。",
                br(),"在重复序列区域，InDel的位置，从信息层面上是不好确定的，举个简单的例子：",
                br(),"这个比对结果：",
                br(),"G - - - AATTCAA",
                br(),"GAAAAATTCAA",
                br(),"跟下面这个比对结果的得分是一样的：",
                br(),"GAA - - - TTCAA",
                br(),"GAAAAATTCAA",
                br(),"这个插入的序列是“AAA”，但是位置是不确定的，它的起点可以是2、3、4，但是我们不会把所有的情况都列出来，只是把最小的起点2作为这个Insertion的起点，后边标记“-2”：Insertion-2，表示这个Insertion的起点有可能在当前起点后2 bp范围内；当移动距离与InDel长度相当或者长度是InDel长度数倍时就在InDel类型前标记上“Dup”。",
              ),
              "ref_length" -> span("Target上SV区域的长度"),
              "query_length" -> span("Query上SV区域的长度"),
              "block_type" -> div("SV所在的比对块类型：",
                br(),"由3个数字组成，第1个数字可取值4,3,2,1,0分别表示：",
                br(),"4-大比对块（几个符合共线性关系的小比对块链接而成的比对块）之间的SV（只能是InDel）",
                br(),"3-大比对块（几个符合共线性关系的小比对块链接而成的比对块）水平上的SV;",
                br(),"2-小比对块水平上的SV;",
                br(),"1-比对块之间的SV;",
                br(),"0-比对块内部的SV.",
                br(),"第2个数字表示：1-倒置，0-非倒置",
                br(),"第3个数字表示：1-易位，0-非易位"
              ),
              "block_id" -> span("SV所在的比对块编号：用“-”连起来的两个数字分别是：大比对块编号、小比对块编号，如果后面的数字是两个小比对块的编号用“.”连起，则表示该SV是位于两个小比对块之间的InDel。"),
              "gene_num" -> span("落在SV上的基因数"),
              "gene_ids" -> span("落在SV上的基因名称，用逗号分隔")
            )
            val titleE = v match {
              case x if (questionMap.isDefinedAt(x)) => {
                span(
                  v,
                  " ", {
                    val popover = Popover(id = "popover-basic")(
                      Popover.Header("说明"),
                      Popover.Body(
                        questionMap(v)
                      )
                    ).rawElement
                    OverlayTrigger(
                      placement = "auto",
                      trigger = js.Array("hover", "focus"),
                      overlay = popover
                    )(
                      span(
                        a(`type` := "button", i(className := "fa fa-question-circle"))
                      )
                    )
                  }
                )()
              }
              case x if !(List("OTU ID").contains(x)) =>
                span(
                  v
                )()
              case x => span(v)()
            }
            Column(
              field = k,
              title = title,
              operable = true,
              searchKind = searchKind,
              formatter = fmt,
              comVF = comVF,
              optionMap = optionMap,
              titleE = titleE
            )
          }.toList
          val curColumns = basicColumns
          val datas = curDatas.map(x => writeJs(x).transform(reader[ujson.Obj]))
          setColumns(curColumns) >>
            setDatas(datas) >>
            props.hasChange(curColumns.nonEmpty)
        }
      }

      def downloadData = (filterDatas: List[ujson.Obj], showColumnNames: List[String]) =>
        IO {
          val downloadColumnNames = showColumnNames.removeAll(List("state", "operate"))
          val filedTitleMap = columns.map(x => (x.field, x.title)).toSeqMap
          val headers = downloadColumnNames.map(x => filedTitleMap(x))
          val contents = filterDatas.map { obj =>
            downloadColumnNames.map { k =>
              obj.value(k).myStr
            }
          }
          val arrays = (headers :: contents)
          Tool.downloadXlsx(s"${fileNamePrefix}.xlsx", arrays)
        }

    }

    useEffect(
      {
        FTool.refreshBasicData
      },
      List(props.id)
    )

    val exportDatas = List(
      ExportData("XLSX", onClick = FTool.downloadData)
    )

    div(
      BootstrapTableCp
        .Props(
          columns = columns,
          datas = datas,
          showExport = true,
          exportData = exportDatas,
          search = true
        )
        .render
    )

  }

  def apply(id: String, hasChange: Boolean => IO[Unit]) = {
    Component(Props(id, hasChange))
  }

}
